#ifndef XLCOMM_NET_PROTOBUF_CODEC_H_
#define XLCOMM_NET_PROTOBUF_CODEC_H_

#include <google/protobuf/message.h>
#include <zlib.h>

#include <functional>
#include <iostream>

#include "xlcomm/base/logger.h"
#include "xlcomm/net/buffer.h"
#include "xlcomm/net/callbacks.h"
#include "xlcomm/net/tcp_connection.h"
using namespace xlcomm;

using MessagePtr = std::shared_ptr<google::protobuf::Message>;
using ProtobufMessageCallback =
    std::function<void(const net::TcpConnectionPtr& conn,
                       const MessagePtr& message, Timestamp recv_time)>;

google::protobuf::Message* CreateMessage(const std::string& type_name);

/*
协议格式如下：
4byte:msg len(以下所有的长度)
4byte:type len
nbyte:type name[type len]
nbyte:protobuf Message
4byte:check sum  // adler32 of type len, type name and protobuf Message
*/
class ProtobufCodec {
 public:
  ProtobufCodec(const ProtobufMessageCallback& cb) : proto_msg_cb_(cb) {
    GOOGLE_PROTOBUF_VERIFY_VERSION;
  }

  void OnMessage(const net::TcpConnectionPtr& conn, net::Buffer* buff,
                 Timestamp recv_time);

  template <typename T>
  void Send(const net::TcpConnectionPtr& conn, const T& message) {
    std::cout << "ProtobufCodec::Send" << std::endl;
    net::Buffer buff;

    std::string type_name = message.GetTypeName();
    int type_len = static_cast<int>(type_name.length() + 1);
    buff.AppendInt32ToNet(type_len);
    buff.Append(type_name);

    std::string msgstr;
    message.SerializeToString(&msgstr);
    buff.Append(msgstr);

    // 少了错误检查、数组相关操作

    int check_sum = static_cast<int>(
        ::adler32(1, reinterpret_cast<const Bytef*>(buff.ReaderPtr()),
                  static_cast<uInt>(buff.ReadableBytes())));
    buff.AppendInt32ToNet(check_sum);

    XLCOMM_ASSERT(buff.ReadableBytes() == sizeof(type_len) + type_len +
                                              msgstr.length() +
                                              sizeof(check_sum));
    buff.PrependInt32ToNet(static_cast<int>(buff.ReadableBytes()));

    conn->Send(&buff);
  }

 private:
  const static int kHeaderLen = sizeof(int);
  const static int kMinMessageLen =
      2 * kHeaderLen + 2;  // type len + type name + check sum
  const static int kMaxMessageLen =
      64 * 1024 * 1024;  // same as codec_stream.h kDefaultTotalBytesLimit

  ProtobufMessageCallback proto_msg_cb_;
};

#endif  // XLCOMM_NET_PROTOBUF_CODEC_H_